﻿using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using static ESF_ABMgr;
/// <summary>
/// 资源加载结果
/// </summary>
public enum enESF_AssetLoadResult
{
    /// <summary>
    /// 未开始
    /// </summary>
    NotStarted,
    /// <summary>
    /// 加载中
    /// </summary>
    Loading,
    /// <summary>
    /// 成功
    /// </summary>
    Success,
    /// <summary>
    /// 失败
    /// </summary>
    Failed,
    /// <summary>
    /// 等待下载
    /// </summary>
    WaitDownload,
    /// <summary>
    /// 取消
    /// </summary>
    Cancel,
}

/// <summary>
/// 资源加载
/// </summary>
public class ESF_AssetLoader : AbsESF_SimulateBehaviour
{
    /// <summary>
    /// 资源加载结果
    /// </summary>
    public enESF_AssetLoadResult assetLoadResult { get; private set; }

    /// <summary>
    /// 资源回应
    /// </summary>
    public ESF_AssetResponse assetResponse { get; private set; }

    /// <summary>
    /// 是否已完成加载流程
    /// </summary>
    public bool isDone { get; private set; }

    /// <summary>
    /// 加载进度
    /// </summary>
    public float progress { get { return mMainAssetProgress + mDepAssetProgress; } }

    /// <summary>
    /// 主资源加载进度
    /// </summary>
    float mMainAssetProgress = 0;

    /// <summary>
    /// 依赖项加载进度
    /// </summary>
    float mDepAssetProgress = 0;

    /// <summary>
    /// 资源管理器
    /// </summary>
    ESF_ABMgr mAbMgr = null;

    /// <summary>
    /// 模拟事件组
    /// </summary>
    protected override int[] simulateBehaviourEvents => new int[] { enESF_SimulateBehaviourEvent.Mono_LateUpdate };

    /// <summary>
    /// 加载资源到内存
    /// </summary>
    ESF_AssetBundleLoadInMemoryFunc mOnLoadInMemory;

    /// <summary>
    /// 获得依赖项资源属性
    /// </summary>
    System.Func<string, ESF_AssetBundlePathAttribute> mOnGetDependencyAbpAttr;

    /// <summary>
    /// 获得Loader
    /// </summary>
    System.Func<int, ESF_AssetLoader> mOnGetLoader = null;

    /// <summary>
    /// 资源包
    /// </summary>
    AssetBundle mAssetBundle = null;

    /// <summary>
    /// 资源
    /// </summary>
    UnityEngine.Object mEditorAsset = null;

    /// <summary>
    /// 当前要加载的依赖项
    /// </summary>
    List<int> mNeedLoadDependencyIds = new List<int>();

    /// <summary>
    /// 依赖项加载进度
    /// </summary>
    Dictionary<int, float> mDependencyProgress = new Dictionary<int, float>();

    /// <summary>
    /// 资源加载步骤
    /// </summary>
    enESF_AssetLoadStep mAssetLoadStep = enESF_AssetLoadStep.Unknown;

    /// <summary>
    /// 资源包创建请求
    /// </summary>
    AssetBundleCreateRequest mAssetBundleCreateRequest = null;

    /// <summary>
    /// 临时依赖属性组
    /// </summary>
    List<ESF_AssetBundlePathAttribute> mTempDepAttrs = new List<ESF_AssetBundlePathAttribute>();

    /// <summary>
    /// 资源加载步骤
    /// </summary>
    enum enESF_AssetLoadStep
    {
        /// <summary>
        /// 未知
        /// </summary>
        Unknown,
        /// <summary>
        /// 等待加载主资源
        /// </summary>
        WaitLoadMain,
        /// <summary>
        /// 等待异步加载资源中
        /// </summary>
        WaitAsyncLoadingMain,
        /// <summary>
        /// 等待加载依赖项
        /// </summary>
        WaitLoadDependency,
    }

    /// <summary>
    /// 销毁
    /// </summary>
    protected override void OnRunDestroy()
    {
        base.OnRunDestroy();
        Recycle();
    }

    /// <summary>
    /// 回收
    /// </summary>
    protected override void OnRecycle()
    {
        base.OnRecycle();
        isDone = false;
        mMainAssetProgress = 0;
        mDepAssetProgress = 0;
        mAssetBundleCreateRequest = null;
        mAssetBundle?.Unload(false);
        mAssetBundle = null;
        mEditorAsset = null;
        mNeedLoadDependencyIds.Clear();
        mDependencyProgress.Clear();
        assetResponse.Recycle();
        mQueueRequest.Clear();
        mTempDepAttrs.Clear();
        assetLoadResult = enESF_AssetLoadResult.NotStarted;
        mAssetLoadStep = enESF_AssetLoadStep.Unknown;
    }

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="_abPathAttr">资源属性</param>
    /// <param name="_abMgr">AB管理器</param>
    /// <param name="_onLoadInMemory">加载资源到内存</param>
    /// <param name="_onGetDependencyAbpAttr">获得依赖项属性</param>
    /// <param name="_onGetLoader">获得Loader</param>
    public ESF_AssetLoader(ESF_AssetBundlePathAttribute _abPathAttr, ESF_ABMgr _abMgr,
        ESF_AssetBundleLoadInMemoryFunc _onLoadInMemory,
        System.Func<string, ESF_AssetBundlePathAttribute> _onGetDependencyAbpAttr,
        System.Func<int, ESF_AssetLoader> _onGetLoader)
    {
        mAbMgr = _abMgr;
        mOnLoadInMemory = _onLoadInMemory;
        mOnGetDependencyAbpAttr = _onGetDependencyAbpAttr;
        mOnGetLoader = _onGetLoader;
        assetLoadResult = enESF_AssetLoadResult.NotStarted;
        assetResponse = new ESF_AssetResponse(_abPathAttr, mAbMgr,
            () => { return mEditorAsset; },
            () => { return mAssetBundle; },
            (abPathAttr) =>
            {
                string sceneName = string.Empty;
                switch (mAbMgr.loadModel)
                {
                    case enESF_AbLoadModel.Editor:
                        sceneName = Path.GetFileNameWithoutExtension(abPathAttr.editorPath);
                        break;
                    default:
                        if (mAssetBundle != null && abPathAttr.isScene)
                        {
                            sceneName = mAssetBundle.GetAllScenePaths()[0];
                        }
                        break;
                }
                return sceneName;
            });
        GameObject go = new GameObject();
        go.hideFlags = mAbMgr.gameObject.hideFlags;
        go.transform.SetParent(mAbMgr.gameObject.transform, false);
        GameObject.DontDestroyOnLoad(go);
        BindGameObject(go);
        OnResetName();
    }


    /// <summary>
    /// 重置名称
    /// </summary>
    void OnResetName()
    {
#if UNITY_EDITOR
        gameObject.name = $"{assetResponse.abPathAttr.assetBundleKey}_{assetResponse.abPathAttr.editorPath}";
#endif
    }

    /// <summary>
    /// 请求队列
    /// </summary>
    Queue<AbsESF_AssetRequest> mQueueRequest = new Queue<AbsESF_AssetRequest>();

    /// <summary>
    /// 重新加载资源
    /// </summary>
    /// <param name="_result"></param>
    public void ReStartLoadAsset(AbsESF_AssetRequest _result)
    {
        Recycle();
        mQueueRequest.Enqueue(_result);
        LoadAsset();
    }

    /// <summary>
    /// 添加资源结果
    /// </summary>
    /// <param name="_result">资源结果</param>    
    public void AddRequest(AbsESF_AssetRequest _result)
    {
        mQueueRequest.Enqueue(_result);
    }

    /// <summary>
    /// 加载资源
    /// </summary>
    public void LoadAsset()
    {
        if (assetLoadResult == enESF_AssetLoadResult.NotStarted)
        {
            assetLoadResult = enESF_AssetLoadResult.Loading;
            switch (mAbMgr.loadModel)
            {
                case enESF_AbLoadModel.Editor:
                    OnLoadAssetByEditor();
                    break;
                default:
                    OnLoadAssetByAssetBundle();
                    break;
            }
        }
    }

    /// <summary>
    /// Editor加载资源
    /// </summary>
    void OnLoadAssetByEditor()
    {
        if (assetResponse.abPathAttr.isScene)
        {
            assetLoadResult = enESF_AssetLoadResult.Success;
            OnSetMainProgress(1);
            isDone = true;
        }
        else
        {
            SFAssetUtility.LoadFromFile(assetResponse.abPathAttr.editorPath, (rst, args) =>
            {
                assetLoadResult = rst != null ? enESF_AssetLoadResult.Success : enESF_AssetLoadResult.Failed;
                mEditorAsset = rst;
                mEditorAsset.name = rst.name;
                OnSetMainProgress(1);
                isDone = true;
            });
        }
    }

    /// <summary>
    /// AssetBundle加载资源
    /// </summary>
    void OnLoadAssetByAssetBundle()
    {
        string abPath = assetResponse.GetAssetBundleFullPath();
        if (assetResponse.isManifestInclude)
        {
            OnLoadDependencies();
        }
        else
        {
            //这里还没处理完，如果能边玩边下则等待下载，否则就是加载失败
            if (mAbMgr.isDownloadWhilePlaying|| mAbMgr.loadModel == enESF_AbLoadModel.CDNAssets)
            {
                assetLoadResult = enESF_AssetLoadResult.WaitDownload;
                assetResponse.AddError($"[WaitDownload] Wait donwload file=>{assetResponse.abPathAttr.assetName}====Req path=>{abPath}");
                stDownLoadFileInfo info = new stDownLoadFileInfo();
                info.HashCode = abPath.GetHashCode();
                info.FileName = assetResponse.abPathAttr.assetName;
                info.UrlPathRoot = SFGlobalSetting.cdnassetBundleRoot;
                info.DownLoadPath = abPath;
                info.Weight = EnGroupWeight.Normal;
                ESF_AssetsDownLoadManager.Instance.AddTask(info, (asset) => {
                    if (asset.Success)
                    {
                        mAssetBundleCreateRequest = AssetBundle.LoadFromMemoryAsync(asset.data);
                        mAssetBundleCreateRequest.completed += MAssetBundleCreateRequest_completed;
                    }
                }, null);

            }
            else
            {
                assetLoadResult = enESF_AssetLoadResult.Failed;
                assetResponse.AddError($"[Failed] Load File =>{assetResponse.abPathAttr.assetName}====Req path=>{abPath}");
                isDone = true;
            }
        }
    }

    /// <summary>
    /// 加载依赖项
    /// </summary>
    void OnLoadDependencies()
    {
        mNeedLoadDependencyIds.Clear();
        mDependencyProgress.Clear();
        mTempDepAttrs.Clear();
        string[] deps = assetResponse.GetAllDependencies();
        if (deps != null && deps.Length > 0)
        {
            mAssetLoadStep = enESF_AssetLoadStep.WaitLoadDependency;
            foreach (var d in deps)
            {
                var attr = mOnGetDependencyAbpAttr?.Invoke(d);
                if (!mNeedLoadDependencyIds.Contains(attr.assetBundleKey))
                {
                    mNeedLoadDependencyIds.Add(attr.assetBundleKey);
                }
                if (!mDependencyProgress.ContainsKey(attr.assetBundleKey))
                {
                    mDependencyProgress.Add(attr.assetBundleKey, 0);
                }
                mTempDepAttrs.Add(attr);
            }
            foreach (var attr in mTempDepAttrs)
            {
                mOnLoadInMemory?.Invoke(attr,
                    (rst, args) =>
                    {
#if UNITY_EDITOR
                        assetResponse.AddMsg($"[Success] Load dependency =>{rst.abPathAttr.assetBundlePath}");
#endif
                    },
                    (rst, args) =>
                    {
                        assetResponse.AddError($"[Failed] Load dependency =>{rst.abPathAttr.assetBundlePath}");
                    },
                    (rst, args) =>
                    {
#if UNITY_EDITOR
                        assetResponse.AddMsg($"[Cancel] Load dependency =>{rst.abPathAttr.assetBundlePath}");
#endif                        
                    },

                    (rst, args) =>
                    {
                        //加载进度
                        mDependencyProgress[rst.abPathAttr.assetBundleKey] = mOnGetLoader.Invoke(rst.abPathAttr.assetBundleKey).progress;
                        float val = 0;
                        foreach (var p in mDependencyProgress.Values)
                        {
                            val += p;
                        }
                        OnSetDepProgress(val);
                    },
                    (rst, args) =>
                      {
#if UNITY_EDITOR
                          assetResponse.AddMsg($"[WaitDownload] Load dependency =>{rst.abPathAttr.assetBundlePath}");
#endif
                     }
                    );
            }
        }
        else
        {
            mAssetLoadStep = enESF_AssetLoadStep.WaitLoadMain;
        }
    }

    /// <summary>
    /// 等待加载依赖项
    /// </summary>
    void OnWaitLoadDependency()
    {
        if (mNeedLoadDependencyIds.Count > 0)
        {
            bool allDone = true;
            foreach (var id in mNeedLoadDependencyIds)
            {
                var loader = mOnGetLoader?.Invoke(id);
                if (loader != null)
                {
                    allDone &= loader.isDone;
                }
                else
                {
                    allDone &= true;
                }
            }
            if (allDone)
            {
                mAssetLoadStep = enESF_AssetLoadStep.WaitLoadMain;
            }
        }
        else
        {
            mAssetLoadStep = enESF_AssetLoadStep.WaitLoadMain;
        }
    }

    /// <summary>
    /// 等待加载主资源
    /// </summary>
    void OnWaitLoadMain()
    {
        string path = assetResponse.abFullPath;
        if (assetResponse.isManifestInclude)
        {
            byte[] assetByte;
            if (ESF_AssetBundleHelper.TryReadBuffer(path, mAbMgr.loadModel, assetResponse.isUseStreamingAssets, out assetByte))
            {
                mAssetBundleCreateRequest = AssetBundle.LoadFromMemoryAsync(assetByte);
                mAssetBundleCreateRequest.completed += MAssetBundleCreateRequest_completed;
                mAssetLoadStep = enESF_AssetLoadStep.WaitAsyncLoadingMain;
            }
            else
            {
                if (mAbMgr.isDownloadWhilePlaying|| mAbMgr.loadModel== enESF_AbLoadModel.CDNAssets)
                {
                    assetLoadResult = enESF_AssetLoadResult.WaitDownload;
                    assetResponse.AddError($"[WaitDownload] Wait donwload file=>{assetResponse.abPathAttr.assetName}====Req path=>{path}");

                    stDownLoadFileInfo info = new stDownLoadFileInfo();
                    info.HashCode = path.GetHashCode();
                    info.FileName = assetResponse.abPathAttr.assetName;
                    info.UrlPathRoot = SFGlobalSetting.cdnassetBundleRoot;
                    info.DownLoadPath = path;
                    info.Weight = EnGroupWeight.Normal;
                    ESF_AssetsDownLoadManager.Instance.AddTask(info, (asset) => {
                        if (asset.Success)
                        {
                            mAssetBundleCreateRequest = AssetBundle.LoadFromMemoryAsync(asset.data);
                            mAssetBundleCreateRequest.completed += MAssetBundleCreateRequest_completed;
                        }
                    }, null);
                    mAssetLoadStep = enESF_AssetLoadStep.WaitAsyncLoadingMain;
                }
                else
                {
                    assetLoadResult = enESF_AssetLoadResult.Failed;
                    assetResponse.AddError($"[Failed] Load File =>{assetResponse.abPathAttr.assetName}====Req path=>{path}");
                    isDone = true;
                }
            }

        }
        else
        {
            assetResponse.AddError($"未在AssetBundleManifest中找到资源=>{assetResponse.abPathAttr.editorPath}=>{assetResponse.abFullPath}");
            assetLoadResult = enESF_AssetLoadResult.Failed;
        }
    }

    /// <summary>
    /// 请求加载资源完成
    /// </summary>
    /// <param name="_async">异步</param>
    private void MAssetBundleCreateRequest_completed(AsyncOperation _async)
    {
        mAssetBundle = mAssetBundleCreateRequest.assetBundle;
        if (mAssetBundle != null && _async.isDone)
        {
            assetLoadResult = enESF_AssetLoadResult.Success;
            mAssetLoadStep = enESF_AssetLoadStep.Unknown;
        }
        else
        {
            assetLoadResult = enESF_AssetLoadResult.Failed;
            assetResponse.AddError($"[Failed] Load Main file =>{assetResponse.abPathAttr.editorPath} ");
        }
        mAssetBundleCreateRequest.completed -= MAssetBundleCreateRequest_completed;
        mAssetBundleCreateRequest = null;
        OnSetMainProgress(1);
        isDone = true;
    }

    /// <summary>
    /// 设置主资源加载进度
    /// </summary>
    /// <param name="_progress">进度</param>
    void OnSetMainProgress(float _progress)
    {
        mMainAssetProgress = Mathf.Clamp01(_progress * 1f / (mNeedLoadDependencyIds.Count + 1));
        OnCallProgress();
    }

    /// <summary>
    /// 设置依赖项加载进度
    /// </summary>    
    /// <param name="_progress">进度</param>
    void OnSetDepProgress(float _progress)
    {
        mDepAssetProgress = Mathf.Clamp01(_progress * (float)mNeedLoadDependencyIds.Count / (mNeedLoadDependencyIds.Count + 1));
        OnCallProgress();
    }

    void OnCallProgress()
    {
        if (mQueueRequest != null && mQueueRequest.Count > 0)
        {
            foreach (var key in mQueueRequest)
            {
                assetResponse.SetProgress(progress);
                key.onProgress?.Invoke(assetResponse, key.extralArgs);
            }
        }
    }

    /// <summary>
    /// OnRunLateUpdate
    /// </summary>
    protected override void OnRunLateUpdate()
    {
        if (mAssetLoadStep != enESF_AssetLoadStep.Unknown)
        {
            switch (mAssetLoadStep)
            {
                case enESF_AssetLoadStep.WaitLoadDependency:
                    OnWaitLoadDependency();
                    break;
                case enESF_AssetLoadStep.WaitLoadMain:
                    OnWaitLoadMain();
                    break;
                case enESF_AssetLoadStep.WaitAsyncLoadingMain:

                    break;
            }
            if (mAssetBundleCreateRequest != null)
            {
                OnSetMainProgress(mAssetBundleCreateRequest.progress);
            }
        }
        if (isDone && mQueueRequest.Count > 0)
        {
            OnResponseQueueRequest();
        }
    }

    /// <summary>
    /// 回应请求队列
    /// </summary>
    void OnResponseQueueRequest()
    {
        while (mQueueRequest.Count > 0)
        {
            AbsESF_AssetRequest req = mQueueRequest.Dequeue();
            if (req.task != null)
            {
                if (req.task.isCancel)
                {
                    assetLoadResult = enESF_AssetLoadResult.Cancel;
                }
                switch (assetLoadResult)
                {
                    case enESF_AssetLoadResult.Success:
                        req.onSuccess?.Invoke(assetResponse, req.extralArgs);
                        break;
                    case enESF_AssetLoadResult.Cancel:
                        req.onCancel?.Invoke(assetResponse, req.extralArgs);
                        break;
                    case enESF_AssetLoadResult.Failed:
                        req.onFailed?.Invoke(assetResponse, req.extralArgs);
                        break;
                    case enESF_AssetLoadResult.WaitDownload:
                        req.onWaitDownloadCallBack?.Invoke(assetResponse, req.extralArgs);
                        break;
                }
                if (assetLoadResult != enESF_AssetLoadResult.WaitDownload)
                {
                    req.Recycle();
                }
            }
        }
    }
}